package com.tmclab.digimaps;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.geotools.data.DataUtilities;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.factory.CommonFactoryFinder;
import org.geotools.feature.FeatureCollections;
import org.geotools.feature.SchemaException;
import org.geotools.feature.simple.SimpleFeatureBuilder;
import org.geotools.geometry.jts.JTSFactoryFinder;
import org.geotools.map.DefaultMapContext;
import org.geotools.map.MapContext;
import org.geotools.styling.StyleFactory;
import org.geotools.swing.JMapFrame;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.GeometryFactory;
import com.vividsolutions.jts.geom.Point;

public class OSMWrapper {
	
	SimpleFeatureType TYPE_POINT = null;
	SimpleFeatureType TYPE_LINE = null;
	SimpleFeatureType TYPE_BUD = null;
	
	SimpleFeatureCollection nodesCollection = FeatureCollections.newCollection();
	SimpleFeatureCollection waysCollection = FeatureCollections.newCollection();
	SimpleFeatureCollection polyCollection = FeatureCollections.newCollection();
	
	SimpleFeatureBuilder fbNode = new SimpleFeatureBuilder(TYPE_POINT);
	SimpleFeatureBuilder fbWay = new SimpleFeatureBuilder(TYPE_LINE);
	SimpleFeatureBuilder fbPoly = new SimpleFeatureBuilder(TYPE_BUD);
	
	private static final String OVERPASS_API = "http://www.overpass-api.de/api/interpreter";
	private static final String OPENSTREETMAP_API_06 = "http://www.openstreetmap.org/api/0.6/";
	
	public OSMWrapper() {
		initTypes();
	}
	
	private void initTypes(){
		try {
			TYPE_POINT = DataUtilities.createType("Location", "location:Point:srid=4326");
			TYPE_LINE = DataUtilities.createType("Line", "lamana:LineString:srid=4326");
		} catch (SchemaException e1) {
			System.out.println("BLAD TYPU");
			e1.printStackTrace();
		}	
	}
	
	public MapContext createMap(){
		List<OSMNode> nodeList = null;
		List<OSMWay> wayList = null;
		
		try {
			Document xml = getXML(18.602856, 54.320462, 18.607562, 54.323349);
			nodeList = getNodes(xml);
			//wayList = getWays(xml);
		} catch (Exception e) {
			System.out.println("BLAD WRAPPERA");
			Logger.getLogger("MAP").log( Level.ALL,"Error: " + e.getMessage());
		}

		GeometryFactory gf = JTSFactoryFinder.getGeometryFactory(null);
		SimpleFeatureBuilder fb = new SimpleFeatureBuilder(TYPE_POINT);
		SimpleFeatureCollection collection = FeatureCollections.newCollection();
		
		for(OSMNode n : nodeList){
			Point point = gf.createPoint(new Coordinate(Double.parseDouble(n.getLon()), Double.parseDouble(n.getLat())));
			fb.add(point);
			SimpleFeature feature = fb.buildFeature(null);
			collection.add(feature);
		}
		

		MapContext map = new DefaultMapContext();
		map.setTitle("Mapa");
		StyleFactory styleFactory = CommonFactoryFinder.getStyleFactory(null);
		map.addLayer(collection,null);
		return map;
	}

	/**
	 * Pobiera dokument XML z serwera OSM i przeksztalca go na obiekt typu Document.
	 * @param lon
	 * @param lat
	 * @param vicinityRange
	 * @return
	 * @throws IOException
	 * @throws SAXException
	 * @throws ParserConfigurationException
	 */
	public static Document getXML(double left, double bottom, double right, double top) throws IOException, SAXException,
		ParserConfigurationException {
		
		DecimalFormat format = new DecimalFormat("##0.0000000", DecimalFormatSymbols.getInstance(Locale.ENGLISH)); //$NON-NLS-1$
		String lefts = format.format(left);
		String bottoms = format.format(bottom);
		String rights = format.format(right);
		String tops = format.format(top);
		
		String string = OPENSTREETMAP_API_06 + "map?bbox=" + lefts + "," + bottoms + "," + rights + ","
				+ tops;
		System.out.println(string);
		URL osm = new URL(string);
		HttpURLConnection connection = (HttpURLConnection) osm.openConnection();
		
		DocumentBuilderFactory dbfac = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder = dbfac.newDocumentBuilder();
		return docBuilder.parse(connection.getInputStream());
	}
	
	/**
	 * Ekstrakcja danych kazdego wezla z przetworzonego dokumentu xml.
	 * @param xmlDocument
	 * @return
	 */
	public static List<OSMNode> getNodes(Document xmlDocument) {
		System.out.println("Dokument ma: " + xmlDocument.getFirstChild().getChildNodes().getLength());
		
		List<OSMNode> osmNodes = new ArrayList<OSMNode>();

		// Document xml = getXML(8.32, 49.001);
		Node osmRoot = xmlDocument.getFirstChild();
		NodeList osmXMLNodes = osmRoot.getChildNodes();
		for (int i = 1; i < osmXMLNodes.getLength(); i++) {
			Node item = osmXMLNodes.item(i);
			if (item.getNodeName().equals("node")) {
				NamedNodeMap attributes = item.getAttributes();
				NodeList tagXMLNodes = item.getChildNodes();
				Map<String, String> tags = new HashMap<String, String>();
				for (int j = 1; j < tagXMLNodes.getLength(); j++) {
					Node tagItem = tagXMLNodes.item(j);
					NamedNodeMap tagAttributes = tagItem.getAttributes();
					if (tagAttributes != null) {
						tags.put(tagAttributes.getNamedItem("k").getNodeValue(), tagAttributes.getNamedItem("v")
								.getNodeValue());
					}
				}
				Node namedItemID = attributes.getNamedItem("id");
				Node namedItemLat = attributes.getNamedItem("lat");
				Node namedItemLon = attributes.getNamedItem("lon");
				Node namedItemVersion = attributes.getNamedItem("version");

				String id = namedItemID.getNodeValue();
				String latitude = namedItemLat.getNodeValue();
				String longitude = namedItemLon.getNodeValue();
				String version = "0";
				if (namedItemVersion != null) {
					version = namedItemVersion.getNodeValue();
				}

				osmNodes.add(new OSMNode(id, latitude, longitude, version, tags));
			}

		}
		return osmNodes;
	}
	
}
